<?php

namespace app\common\controller;

use app\api\controller\ApiNotify;
use think\facade\Env;
use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Util\PemUtil;
use think\App;
use think\facade\Request;
use think\facade\Request as FacadeRequest;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Formatter;

/**
 * 微信支付
 */
class WechatPay
{
  protected $instance = NULL;

  /**
   * 初始化方法
   */
  public  function __construct()
  {
    // 商户号
    $merchantId =  Env::get('WECHAT.PAY_MCHID');
    // 从本地文件中加载「商户API私钥」，「商户API私钥」会用来生成请求的签名
    $merchantPrivateKeyFilePath = 'file://' . app()->getRootPath() . 'public/cert/wechat/' . Env::get('WECHAT.PAY_PRIVATEEKY_PATH');
    $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
    // 「商户API证书」的「证书序列号」
    $merchantCertificateSerial = Env::get('WECHAT.PAY_CERTIFICATESERIAL');
    // 从本地文件中加载「微信支付平台证书」(可使用证书下载工具得到），用来验证微信支付应答的签名
    $platformCertificateFilePath = 'file://' . app()->getRootPath() . 'public/cert/wechat/' . Env::get('WECHAT.PAY_CERTIFICATEPATH');
    $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
    // 从「微信支付平台证书」中获取「证书序列号」
    $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
    // 构造一个 APIv3 客户端实例
    $this->instance = Builder::factory([
      'mchid'      => $merchantId,
      'serial'     => $merchantCertificateSerial,
      'privateKey' => $merchantPrivateKeyInstance,
      'certs'      => [
        $platformCertificateSerial => $platformPublicKeyInstance,
      ],
    ]);
  }

  //订单退款
  public function orderRefund($order_no, $out_refund_no, $money)
  {
    $money = intval(round($money * 100));
    try {
      $resp = $this->instance->chain('v3/refund/domestic/refunds')
        ->post(['json' => [
          'out_trade_no'  => $order_no,
          'out_refund_no' => $out_refund_no,
          'amount'        => [
            'refund'   => $money,
            'total'    => $money,
            'currency' => 'CNY'
          ],
        ]]); 
      $result_code = $resp->getStatusCode();
      if ($result_code == 200) {
        $result_data = json_decode($resp->getBody(), true);
        return ['code'  => 200, 'msg'  =>  '成功', 'data' => $result_data];
      } else {
        return ['code'  => 400, 'msg'  =>  '失败', 'data' => []];
      }
    } catch (\Exception $e) { 
      // 进行错误处理 
      return ['code'  => 400, 'msg'  =>  '失败', 'data' => []];
    } 
  }


  //小程序支付
  public function miniProPay($money, $order_no, $openid)
  {
    // 获取当前域名
    $domain = Request::domain();
    // 获取当前协议（http或https）
    $scheme = Request::scheme();
    // 获取当前端口
    $port = Request::port();
    // 如果是非标准端口（80或443），则需要加上端口号
    if ($scheme == 'http' && $port != 80 || $scheme == 'https' && $port != 443) {
      $domain .= ':' . $port;
    }
    $money = intval(round($money * 100));
    try {
      $resp = $this->instance->chain('v3/pay/transactions/jsapi')
        ->post(['json' => [
          'mchid'        => Env::get('WECHAT.PAY_MCHID'),
          'out_trade_no' => $order_no,
          'appid'        => Env::get('WECHAT.WECHAT_APPID'),
          'description'  => '订单支付',
          'notify_url'   => $domain . '/api/ApiNotify/wechatPayNotify',
          'amount'       => [
            'total'    => $money,
            'currency' => 'CNY'
          ],
          "payer" => [
            "openid" => $openid
          ]
        ]]);
      $result_code = $resp->getStatusCode();
      if ($result_code == 200) {
        $result_data = json_decode($resp->getBody(), true);
        // 生成支付签名
        $arouse_data = $this->generateSignature($result_data);
        return ['code'  => 200, 'msg'  =>  '成功', 'data' => $arouse_data];
      } else {
        return ['code'  => 400, 'msg'  =>  '失败', 'data' => []];
      }
    } catch (\Exception $e) {
      // 进行错误处理 
      if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
        $r = $e->getResponse();
        $errorRst = json_decode($r->getBody(), true);
        return ['code'  => 400, 'msg'  => $errorRst['message'], 'data' => []];
      }
    }
  }


  /**
   * 生成支付签名
   * @param $result_data
   * @return array
   */
  public function generateSignature($result_data)
  {
    // 从本地文件中加载「商户API私钥」，「商户API私钥」会用来生成请求的签名
    // 文件路径例如：D:\EMin\xxx\cert\wx_v3\v3_apiclient_key.pem
    $merchantPrivateKeyInstance = Rsa::from('file://' . app()->getRootPath() . 'public/cert/wechat/' . Env::get('WECHAT.PAY_PRIVATEEKY_PATH'), Rsa::KEY_TYPE_PRIVATE);
    $arouse_data = [
      'appId' => Env::get('WECHAT.WECHAT_APPID'),
      'timeStamp' => strval(Formatter::timestamp()),
      'nonceStr' => Formatter::nonce(),
      // 'prepayId' => $result_data['prepay_id'],
      'package'   => 'prepay_id=' . $result_data['prepay_id'],
    ];

    $arouse_data += ['paySign' => Rsa::sign(
      Formatter::joinedByLineFeed(...array_values($arouse_data)),
      $merchantPrivateKeyInstance
    ), 'signType' => 'RSA'];

    return $arouse_data;
  }

  /**
   * v3版本的支付回调
   * @return false|mixed|string
   */
  public function notify()
  {
    //获取请求参数
    $notifyData = file_get_contents('php://input');
    //获取请求头用来签名验签
    $header = FacadeRequest::header();
    // 转换通知的JSON文本消息为PHP Array数组
    $inBodyArray = (array)json_decode($notifyData, true);

    if ($inBodyArray['event_type'] !== 'TRANSACTION.SUCCESS') {
      return json_encode(['code' => 400, 'message' => '支付失败']);
    }

    //签名验签
    $signStatus = $this->signVerify($header, $notifyData);
    if (!$signStatus) {
      return json_encode(['code' => 400, 'message' => '验签失败']);
    }
    // 使用PHP7的数据解构语法，从Array中解构并赋值变量
    ['resource' => [
      'ciphertext' => $ciphertext,
      'nonce' => $nonce,
      'associated_data' => $aad
    ]] = $inBodyArray;

    // 加密文本消息解密
    $inBodyResource = AesGcm::decrypt($ciphertext, Env::get('WECHAT.PAY_APIV3'), $nonce, $aad);

    // 把解密后的文本转换为PHP Array数组
    $inBodyResourceArray = (array)json_decode($inBodyResource, true);

    $apiNotify = new ApiNotify();
    $out_trade_no = $inBodyResourceArray['out_trade_no'];
    $transaction_id = $inBodyResourceArray['transaction_id'];
    $payRst = $apiNotify->doNotify($out_trade_no, $transaction_id);
    if ($payRst) {
      return json_encode(['code' => 200]);
    } else {
      return json_encode(['code' => 400]);
    }
  }

  /**签名验签
   * @param $header 请求头
   * @param $body  请求参数
   * @return bool
   */
  public function signVerify($header, $body)
  {
    $inWechatpaySignature = $header['wechatpay-signature']; //head(); // 请根据实际情况获取 微信方的签名
    $inWechatpayTimestamp = $header['wechatpay-timestamp']; //head(); // 请根据实际情况获取 微信方的时间戳
    $inWechatpayNonce = $header['wechatpay-nonce']; //head(); // 请根据实际情况获取 微信方的随机字符串

    // 根据通知的平台证书序列号，查询本地平台证书文件，
    $platformPublicKeyInstance = Rsa::from('file://' . app()->getRootPath() . 'public/cert/wechat/' . Env::get('WECHAT.PAY_CERTIFICATEPATH'), Rsa::KEY_TYPE_PUBLIC);

    // 检查通知时间偏移量，允许5分钟之内的偏移
    $timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
    $verifiedStatus = Rsa::verify(
      // 构造验签名串
      Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $body),
      $inWechatpaySignature,
      $platformPublicKeyInstance
    );
    if ($timeOffsetStatus && $verifiedStatus) {
      return true;
    }
    return false;
  }
}
